package com.itmk.config.security.filter;

import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.itmk.config.security.detailservice.CustomerUserDetailService;
import com.itmk.config.security.exception.CustomerAuthenionException;
import com.itmk.config.security.handler.LoginFailureHandler;
import com.itmk.jwt.JwtUtils;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.rmi.server.ServerCloneException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Data
@Component("checkTokenFilter")
public class CheckTokenFilter extends OncePerRequestFilter {
    @Value("#{'${ignore.url}'.split(',')}")
    private List<String> ignoreUrl;
    @Autowired
    private LoginFailureHandler loginFailureHandler;
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private CustomerUserDetailService customerUserDetailService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException{
        try{
            String url = request.getRequestURI();
            if (!ignoreUrl.contains(url) && !url.contains("/images/")&& !url.startsWith("/wxapi/allApi/")){
                validateToken(request);
            }
        }catch (AuthenticationException e){
            loginFailureHandler.commence(request,response,e);
            return;
        }
        filterChain.doFilter(request,response);
    }
    protected void validateToken(HttpServletRequest request) throws AuthenticationException {
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
        }
        if (StringUtils.isEmpty(token)) {
            throw new BadCredentialsException("请传递token");
        }
        if (!jwtUtils.verify(token)) {
            throw new BadCredentialsException("非法token");
        }

        // 解析 Token
        DecodedJWT decodedJWT = jwtUtils.jwtDecode(token);
        if (decodedJWT == null) {
            throw new BadCredentialsException("Token解析失败");
        }

        Map<String, Claim> claims = decodedJWT.getClaims();
        if (claims == null) {
            throw new BadCredentialsException("Token claims为空");
        }

        Claim usernameClaim = claims.get("username");
        if (usernameClaim == null || StringUtils.isEmpty(usernameClaim.asString())) {
            throw new BadCredentialsException("Token缺少username信息");
        }

        String username = usernameClaim.asString();
        UserDetails details = customerUserDetailService.loadUserByUsername(username);
        if (details == null) {
            throw new UsernameNotFoundException("用户不存在: " + username);
        }

        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(details, null, details.getAuthorities());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}
